package com.realestate.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.log4j.Logger;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import com.mysema.query.BooleanBuilder;
import com.mysema.query.jpa.JPQLQuery;
import com.mysema.query.jpa.JPQLSubQuery;
import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.expr.BooleanExpression;
import com.realestate.constants.AppConstant;
import com.realestate.constants.AppConstant.PRICE_SEARCH;
import com.realestate.constants.ComUnitName;
import com.realestate.constants.RecordStatus;
import com.realestate.model.BrtbProduct;
import com.realestate.model.QBrtbAttribute;
import com.realestate.model.QBrtbBuilding;
import com.realestate.model.QBrtbCategory;
import com.realestate.model.QBrtbDistrict;
import com.realestate.model.QBrtbProduct;
import com.realestate.model.QBrtbProductAttributeValue;
import com.trg.dao.jpa.GenericDAOImpl;

@Repository
public class ProductQueryDaoImpl extends GenericDAOImpl<BrtbProduct, Long>
		implements ProductQueryDao {

	private static final Logger logger = Logger
			.getLogger(ProductQueryDaoImpl.class);

	@Override
	@PersistenceContext(unitName = ComUnitName.COM_UNIT)
	public void setEntityManager(EntityManager entityManager) {
		super.setEntityManager(entityManager);
	}

	/**
	 * Get product has the sam category
	 * 
	 * @param category
	 *            : the same category not null
	 * @param limit
	 *            : the number items not null
	 * @param productId
	 *            : get the related product (except it)
	 * @param typeProduct
	 *            for rent or for sale
	 */
	@Override
	public List<BrtbProduct> getListproductByCategoryCode(String categoryCode,
			Long limit, Long productId, String typeProduct) {
		JPQLQuery query = new JPAQuery(em());

		QBrtbProduct product = QBrtbProduct.brtbProduct;
		QBrtbCategory category = QBrtbCategory.brtbCategory;

		BooleanBuilder where = new BooleanBuilder();
		if (categoryCode != null && !categoryCode.equals(AppConstant.ALL)) {
			where.and(category.categoryCode.eq(categoryCode));
		}
		where.and(category.recordStatus.eq(RecordStatus.O)).and(
				product.recordStatus.eq(RecordStatus.O));
		if (productId != null) {
			where.and(product.id.ne(productId));
		}
		if (typeProduct != null) {
			where.and(product.typeProduct.eq(typeProduct));
		}
		query.from(product).leftJoin(product.brtbCategory, category)
				.where(where).orderBy(product.makeDate.desc()).limit(limit);

		List<BrtbProduct> products = query.list(product);
		return products;
	}

	@Override
	public List<BrtbProduct> getListproductByCategoryCode(String categoryCode,
			Map<String, String> filter, Long limit) {
		JPQLQuery query = new JPAQuery(em());

		QBrtbProduct product = QBrtbProduct.brtbProduct;
		QBrtbCategory category = QBrtbCategory.brtbCategory;

		BooleanBuilder where = new BooleanBuilder();
		if (categoryCode != null && !categoryCode.equals(AppConstant.ALL)) {
			where.and(category.categoryCode.eq(categoryCode));
		}
		JPQLSubQuery subQuery = new JPQLSubQuery();
		QBrtbProductAttributeValue subBrtbProductAttributeValue = new QBrtbProductAttributeValue(
				"subBrtbProductAttributeValue");
		subQuery.from(subBrtbProductAttributeValue);
		List<BooleanExpression> lstBooleanExpressions = new ArrayList<BooleanExpression>();
		for (Entry<String, String> entry : filter.entrySet()) {
			lstBooleanExpressions
					.add(subBrtbProductAttributeValue.brtbAttribute.attributeCode
							.eq(entry.getKey()).and(
									subBrtbProductAttributeValue.strValue
											.eq(entry.getValue())));
			subQuery.where(BooleanExpression.anyOf(lstBooleanExpressions
					.toArray(new BooleanExpression[lstBooleanExpressions.size()])));
		}
		where.and(product.id.in(subQuery
				.list(subBrtbProductAttributeValue.brtbProduct.id)));
		where.and(category.recordStatus.eq(RecordStatus.O)).and(
				product.recordStatus.eq(RecordStatus.O));

		QBrtbProductAttributeValue brtbProductAttributeValue = QBrtbProductAttributeValue.brtbProductAttributeValue;
		QBrtbAttribute brtbAttribute = QBrtbAttribute.brtbAttribute;
		query.from(product)
				.leftJoin(product.brtbCategory, category)
				.where(where)
				.orderBy(product.makeDate.desc())
				.limit(limit)
				.leftJoin(product.brtbProductAttributeValues,
						brtbProductAttributeValue)
				.leftJoin(brtbProductAttributeValue.brtbAttribute,
						brtbAttribute);

		List<BrtbProduct> products = query.list(product);
		return products;
	}

	@Override
	public Page<BrtbProduct> getPagingProduct(Pageable pageable,
			String categoryCode) {
		JPQLQuery query = new JPAQuery(em());

		QBrtbProduct product = QBrtbProduct.brtbProduct;
		QBrtbCategory category = QBrtbCategory.brtbCategory;

		BooleanBuilder where = new BooleanBuilder();
		where.and(category.recordStatus.eq(RecordStatus.O)).and(
				product.recordStatus.eq(RecordStatus.O));
		if (categoryCode != null && !categoryCode.equals(AppConstant.ALL)) {
			where.and(category.categoryCode.eq(categoryCode));
		}

		query.from(product).leftJoin(product.brtbCategory, category)
				.where(where).orderBy(product.makeDate.desc());
		query.offset(pageable.getOffset()).limit(pageable.getPageSize());

		List<BrtbProduct> products = query.list(product);
		long totalRows = query.from(product)
				.leftJoin(product.brtbCategory, category).where(where).count();
		PageImpl<BrtbProduct> result = new PageImpl<BrtbProduct>(products,
				pageable, totalRows);
		return result;
	}

	@Override
	public Page<BrtbProduct> getPagingProductSimpleSearch(Pageable pageable,
			Map<String, String> attributes) {
		Boolean categoryFlag = Boolean.valueOf("false");
		JPQLQuery query = new JPAQuery(em());
		JPQLQuery queryCount = new JPAQuery(em());
		
		QBrtbProduct product = QBrtbProduct.brtbProduct;
		QBrtbCategory category = QBrtbCategory.brtbCategory;
		QBrtbDistrict district = QBrtbDistrict.brtbDistrict;
		QBrtbBuilding building = QBrtbBuilding.brtbBuilding;
		
		BooleanBuilder where = new BooleanBuilder();
		
		query.from(product);
		queryCount.from(product);
		// filter
		if(attributes.get(AppConstant.PRODUCTNAME) != null){
			where.and(product.productName.like("%"+ attributes.get(AppConstant.PRODUCTNAME) + "%"));
		}
		if(attributes.get(AppConstant.RENT) != null){
			 where.and(product.typeProduct.eq(AppConstant.RENT));
		}
		if(attributes.get(AppConstant.SALE) != null){
			 where.and(product.typeProduct.eq(AppConstant.SALE));
		}
		if(attributes.get(AppConstant.APARTMENT) != null || attributes.get(AppConstant.HOUSE) != null 
					|| attributes.get(AppConstant.VILLA) != null || attributes.get(AppConstant.RESORT) != null 
					|| attributes.get(AppConstant.SERVICED_APARTMENT) != null){
			 query.leftJoin(product.brtbCategory, category);
			 queryCount.leftJoin(product.brtbCategory, category);
			 
			 if(attributes.get(AppConstant.APARTMENT) != null){
				 categoryFlag = Boolean.TRUE;
				 where.and(category.categoryCode.eq(AppConstant.APARTMENT));
			 }
			 if(attributes.get(AppConstant.HOUSE) != null){
				 if(categoryFlag){
					 where.or(category.categoryCode.eq(AppConstant.HOUSE));
				 }else{
					 where.and(category.categoryCode.eq(AppConstant.HOUSE));
				 }
			 }
			 if(attributes.get(AppConstant.VILLA) != null){
				 if(categoryFlag){
					 where.or(category.categoryCode.eq(AppConstant.VILLA)); 
				 }else{
					 where.and(category.categoryCode.eq(AppConstant.VILLA)); 
				 }
			 }
			 if(attributes.get(AppConstant.RESORT) != null ){
				 if(categoryFlag){
					 where.or(category.categoryCode.eq(AppConstant.RESORT));
				 }else{
					 where.and(category.categoryCode.eq(AppConstant.RESORT));
				 }
			 }
			 if(attributes.get(AppConstant.SERVICED_APARTMENT) != null){
				 if(categoryFlag){
					 where.or(category.categoryCode.eq(AppConstant.SERVICED_APARTMENT));
				 }else{
					 where.and(category.categoryCode.eq(AppConstant.SERVICED_APARTMENT));
				 }
			 }
		}
		if(attributes.get(AppConstant.BEDROOM) != null){
			if(!attributes.get(AppConstant.BEDROOM).equals(AppConstant.NOCHOOSE)){
				Integer bedroomNumber = Integer.parseInt(attributes.get(AppConstant.BEDROOM));
				where.and(product.bedroom.eq(bedroomNumber));
			}
		}
		if(attributes.get(AppConstant.PRICE) != null){
			if(!attributes.get(AppConstant.PRICE).equals(AppConstant.NOCHOOSE)){
				switch (PRICE_SEARCH.valueOf(attributes.get(AppConstant.PRICE))) {
					case UNDER_ONE:
						where.and(product.price.lt(AppConstant.ONE_THOUSAND));
						break;
					case ONE_TWO:
						where.and(product.price.goe(AppConstant.ONE_THOUSAND)).and(product.price.loe(AppConstant.TWO_THOUSAND));
						break;
					case TWO_THREE:
						where.and(product.price.goe(AppConstant.TWO_THOUSAND)).and(product.price.loe(AppConstant.THREE_THOUSAND));
						break;
					case THREE_FOUR:
						where.and(product.price.goe(AppConstant.THREE_THOUSAND)).and(product.price.loe(AppConstant.FOUR_THOUSAND));
						break;
					case ABOVE_FOUR:
						where.and(product.price.gt(AppConstant.FOUR_THOUSAND));
						break;
					default:
						break;
				}
			}
		}
		
		if(attributes.get(AppConstant.DISTRICT) != null){
			if(!attributes.get(AppConstant.DISTRICT).equals(AppConstant.NOCHOOSE)){
				where.and(district.districtCode.eq(attributes.get(AppConstant.DISTRICT)));
				query.leftJoin(product.brtbDistrict, district);
				queryCount.leftJoin(product.brtbDistrict, district);
			}
		}
		if(attributes.get(AppConstant.BUILDING) != null){
			if(!attributes.get(AppConstant.BUILDING).equals(AppConstant.NOCHOOSE)){
				where.and(building.buildingCode.eq(attributes.get(AppConstant.BUILDING)));
				query.leftJoin(product.brtbBuilding, building);
				queryCount.leftJoin(product.brtbBuilding, building);
			}
		}
		query.where(where).orderBy(product.makeDate.desc());
		query.offset(pageable.getOffset()).limit(pageable.getPageSize());
		
		List<BrtbProduct> products = query.list(product);
		long totalRows = queryCount.where(where).count();
		PageImpl<BrtbProduct> result = new PageImpl<BrtbProduct>(products, pageable, totalRows);
		
		return result;
	}

	@Override
	public Page<BrtbProduct> getPagingProduct(Pageable pageable,
			String categoryCode, String districtCode, String buildingCode) {
		JPQLQuery query = new JPAQuery(em());
		JPQLQuery queryCount = new JPAQuery(em());
		
		QBrtbProduct product = QBrtbProduct.brtbProduct;
		QBrtbCategory category = QBrtbCategory.brtbCategory;
		QBrtbDistrict district = QBrtbDistrict.brtbDistrict;
		QBrtbBuilding building = QBrtbBuilding.brtbBuilding;
		
		BooleanBuilder where = new BooleanBuilder();
		where.and(product.recordStatus.eq(RecordStatus.O));
		query.from(product);
		queryCount.from(product);
		
		if (categoryCode != null && !categoryCode.equals(AppConstant.ALL)) {
			where.and(category.categoryCode.eq(categoryCode));
			where.and(category.recordStatus.eq(RecordStatus.O));
			query.leftJoin(product.brtbCategory, category);
			queryCount.leftJoin(product.brtbCategory, category);
		}
		if (districtCode != null) {
			where.and(district.districtCode.eq(districtCode));
			where.and(district.recordStatus.eq(RecordStatus.O));
			query.leftJoin(product.brtbDistrict, district);
			queryCount.leftJoin(product.brtbDistrict, district);
		}
		if(buildingCode != null){
			where.and(building.buildingCode.eq(buildingCode));
			where.and(building.recordStatus.eq(RecordStatus.O));
			query.leftJoin(product.brtbBuilding, building);
			queryCount.leftJoin(product.brtbBuilding, building);
		}
		
		
		query.where(where).orderBy(product.makeDate.desc());
		query.offset(pageable.getOffset()).limit(pageable.getPageSize());

		List<BrtbProduct> products = query.list(product);
		long totalRows = queryCount.where(where).count();
		PageImpl<BrtbProduct> result = new PageImpl<BrtbProduct>(products,
				pageable, totalRows);
		return result;
	}
}
